<?php

namespace yunj\core;

use think\facade\Request;
use yunj\core\builder\YunjForm;
use yunj\core\enum\Def;
use yunj\enum\TipsTemplet;
use think\Validate as ThinkValidate;
use think\exception\HttpResponseException;
use yunj\core\exception\ResponseJsonException;

class Validate extends ThinkValidate {

    /**
     * 校验数据
     * @var array
     */
    protected $data = [];

    /**
     * 自动处理，默认关闭
     * @var bool
     */
    protected $auto = false;

    /**
     * 错误编码
     * @var int
     */
    protected $errorCode = 0;

    /**
     * 错误响应数据
     * @var array
     */
    protected $errorResponseData = [];

    /**
     * 设置所有验证字段规则
     * @param array $rule
     */
    public function setAttrRule(array $rule): void {
        $this->rule = $rule;
    }

    /**
     * 设置所有验证字段描述
     * @param array $field
     */
    public function setAttrField(array $field): void {
        $this->field = $field;
    }

    /**
     * 设置所有验证环境
     * @param array $scene
     */
    public function setAttrScene(array $scene): void {
        $this->scene = $scene;
    }

    /**
     * 获取所有验证环境
     * @return array
     */
    public function getAttrScene(): array {
        return $this->scene;
    }

    /**
     * Notes: 获取错误提示
     * Author: Uncle-L
     * Date: 2021/11/13
     * Time: 13:44
     * @return string
     */
    public function getError(): string {
        $error = $this->error;
        return is_string($error) ? $error : implode(";", $error);
    }

    /**
     * 获取字段对应的错误信息
     * @return array|string 批量验证时返回数组
     */
    public function getFieldError() {
        return $this->error;
    }

    /**
     * 获取错误编码
     */
    public function getErrorCode() {
        return $this->errorCode;
    }

    /**
     * 获取错误响应json
     * @return \think\response\Json
     */
    public function getErrorResponseJson() {
        if ($this->errorResponseData) {
            ['errcode' => $errcode, 'msg' => $msg, 'data' => $data] = $this->errorResponseData;
            $response = response_json($errcode, $msg, $data);
        } else {
            $response = error_json($this->getError());
        }
        return $response;
    }

    /**
     * Notes: 获取校验数据
     * Author: Uncle-L
     * Date: 2021/11/13
     * Time: 13:44
     * @return string
     */
    public function getData(): array {
        return $this->data;
    }

    /**
     * Notes: 自动处理
     * Author: Uncle-L
     * Date: 2020/11/1
     * Time: 20:40
     * @param bool $enable 调用此方法，参数留空默认开启
     * @return $this
     */
    public function auto(bool $enable = true) {
        $this->auto = $enable;
        return $this;
    }

    /**
     * 初始化属性
     * @param array $data
     */
    protected function initAttr(array $data): void {
        $this->error = [];
        $this->data = $data;
        $this->responseJsonException = null;
    }

    /**
     * Notes: 数据自动验证（重写，增加数据处理功能）
     * Author: Uncle-L
     * Date: 2020/11/1
     * Time: 20:49
     * @param array $data
     * @param array $rules
     * @param string $scene
     * @return bool
     */
    public function check(array $data, $rules = []): bool {
        $this->initAttr($data);
        // 验证结果
        $res = parent::check($data, $rules);
        if ($res) {
            // 验证通过，进行数据处理
            try {
                $this->handleData($this->data, $this->currentScene);
                return true;
            } catch (ResponseJsonException $e) {
                $errorData = $e->getResponse()->getData();
                $error = $errorData["msg"];
                $this->error = $this->batch ? array_merge($this->error, [$error]) : $error;
                $errorData["msg"] = $this->getError();
                $this->errorResponseData = $errorData;
            }
        }
        // 验证失败，设置错误编码
        $errorCode = 10000;
        if (isset($errorData['errcode'])) {
            $errorCode = $errorData['errcode'];
        }
        $this->errorCode = $errorCode;
        // 验证失败，自动处理错误结果并响应
        if ($this->auto) {
            Request::isAjax() ? throw_json($this->getErrorResponseJson()) : throw_redirect(tips_url(TipsTemplet::ERROR(), $msg));
        }
        return false;
    }

    /**
     * Notes: 可对校验的数据进行额外的处理、赋值等操作，并返回
     * Author: Uncle-L
     * Date: 2020/1/16
     * Time: 17:22
     * @param array $data [原始数据]
     * @param string|mixed $scene [验证场景]
     * @throws HttpResponseException
     */
    protected function handleData(array &$data, $scene): void {
    }

    /**
     * Notes: 组装错误消息
     * Author: Uncle-L
     * Date: 2021/10/21
     * Time: 16:23
     * @param string $field [字段名]
     * @param string $title [字段描述]
     * @param string $error [错误信息]
     * @return string
     */
    protected function packageError(string $field, string $title, string $error): string {
        return ($title ?: $field) . $error;
    }

    /**
     * Notes: 正整数验证
     * Author: Uncle-L
     * Date: 2021/10/21
     * Time: 16:06
     * @param $value [验证数据]
     * @param string $rule [验证规则]
     * @param array $data [全部数据]
     * @param string $field [字段名]
     * @param string $title [字段描述]
     * @return bool|string
     */
    protected function positiveInt($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (is_positive_int($value)) return true;
        return $this->packageError($field, $title, "需为正整数");
    }

    /**
     * Notes: 正整数验证
     * Author: Uncle-L
     * Date: 2021/10/21
     * Time: 16:06
     * @param $value [验证数据]
     * @param string $rule [验证规则]
     * @param array $data [全部数据]
     * @param string $field [字段名]
     * @param string $title [字段描述]
     * @return bool|string
     */
    protected function positiveInteger($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (is_numeric($value) && is_int($value + 0) && ($value + 0) > 0) {
            return true;
        }
        return $this->packageError($field, $title, "需为正整数");
    }

    /**
     * Notes: 非负整数验证
     * Author: Uncle-L
     * Date: 2021/10/21
     * Time: 16:15
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function nonnegativeInt($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (is_nonnegative_int($value)) return true;
        return $this->packageError($field, $title, "非负整数");
    }

    /**
     * Notes: 非负整数验证
     * Author: Uncle-L
     * Date: 2021/10/21
     * Time: 16:15
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function nonnegativeInteger($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (is_nonnegative_integer($value)) return true;
        return $this->packageError($field, $title, "非负整数");
    }

    /**
     * Notes: 非负数验证
     * Author: Uncle-L
     * Date: 2021/10/21
     * Time: 16:15
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function nonnegativeNum($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (is_nonnegative_num($value)) return true;
        return $this->packageError($field, $title, "非负数");
    }

    /**
     * Notes: 非负数验证
     * Author: Uncle-L
     * Date: 2021/10/21
     * Time: 16:15
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function nonnegativeNumber($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (is_nonnegative_num($value)) return true;
        return $this->packageError($field, $title, "非负数");
    }

    /**
     * Notes: 验证一维数组里面的值只能为rule的值
     * Author: Uncle-L
     * Date: 2020/2/16
     * Time: 23:02
     * @param $value [待验证数组]
     * @param string $rule [验证规则字符串]
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     * 使用实例：
     *          protected $rule = [
     *              'hobby' => 'array|arrayIn:read,write',
     *          ];
     *          protected $message = [
     *              'hobby.array' => '参数[hobby]格式错误',
     *              'hobby.arrayIn' => '参数[hobby]错误',
     *          ];
     */
    protected function arrayIn($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_array($value)) return $this->packageError($field, $title, "格式错误");
        if (!$value) return $this->packageError($field, $title, "不能为空");
        if (!$rule) return $this->packageError($field, $title, "验证规则[arrayIn]错误");
        $ruleArr = explode(',', $rule);
        $res = array_in($value, $ruleArr);
        if (!$res) return $this->packageError($field, $title, "元素需在指定范围[{$rule}]内");
        return true;
    }

    /**
     * Notes: 验证一维数组为空或者里面的值只能为rule的值
     * Author: Uncle-L
     * Date: 2020/2/16
     * Time: 23:02
     * @param $value [待验证数组]
     * @param string $rule [验证规则字符串]
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     * 使用实例：
     *          protected $rule = [
     *              'hobby' => 'arrayEmptyOrIn:read,write',
     *          ];
     *          protected $message = [
     *              'hobby.arrayEmptyOrIn' => '参数[hobby]错误',
     *          ];
     */
    protected function arrayEmptyOrIn($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_array($value)) return $this->packageError($field, $title, "格式错误");
        if (!$value) return true;
        if (!$rule || !strstr($rule, ',')) return $this->packageError($field, $title, "验证规则[arrayEmptyOrIn]错误");
        $ruleArr = explode(',', $rule);
        $res = array_in($value, $ruleArr);
        if (!$res) return $this->packageError($field, $title, "元素需在指定范围[{$rule}]内");
        return true;
    }

    /**
     * Notes: 验证一维数组里面的值只能为正整数
     * Author: Uncle-L
     * Date: 2021/10/21
     * Time: 16:13
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function arrayPositiveInt($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_array($value)) return $this->packageError($field, $title, "格式错误");
        if (!$value) return $this->packageError($field, $title, "不能为空");
        if (is_positive_int_array($value)) return true;
        return $this->packageError($field, $title, "需为正整数数组");
    }

    /**
     * Notes: 验证一维数组为空或值只能为正整数
     * Author: Uncle-L
     * Date: 2020/2/16
     * Time: 23:02
     * @param $value [待验证数组]
     * @param string $rule [验证规则字符串]
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     * 使用实例：
     *          protected $rule = [
     *              'hobby' => 'arrayEmptyOrPositiveInt',
     *          ];
     *          protected $message = [
     *              'hobby.arrayEmptyOrPositiveInt' => '参数[hobby]错误',
     *          ];
     */
    protected function arrayEmptyOrPositiveInt($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_array($value)) return $this->packageError($field, $title, "格式错误");
        if (!$value) return true;
        if (is_positive_int_array($value)) return true;
        return $this->packageError($field, $title, "需为正整数数组");
    }

    /**
     * Notes: map一维数组的key必须包含给定的规则里面的值
     * Author: Uncle-L
     * Date: 2021/10/21
     * Time: 16:13
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function mapHas($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_array($value)) return $this->packageError($field, $title, "格式错误");
        if (!$value) return $this->packageError($field, $title, "不能为空");
        if (!$rule || !strstr($rule, ',')) return $this->packageError($field, $title, "验证规则[mapHas]错误");
        $ruleArr = explode(',', $rule);
        $valueKeys = array_keys($value);
        if (array_diff($ruleArr, $valueKeys)) return $this->packageError($field, $title, "错误");
        return true;
    }

    /**
     * Notes: map一维数组的key必须包含给定的规则里面的值
     * Author: Uncle-L
     * Date: 2021/10/21
     * Time: 16:13
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function mapEmptyOrHas($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_array($value)) return $this->packageError($field, $title, "格式错误");
        if (!$value) return true;
        if (!$rule || !strstr($rule, ',')) return $this->packageError($field, $title, "验证规则[mapHas]错误");
        $ruleArr = explode(',', $rule);
        $valueKeys = array_keys($value);
        if (array_diff($ruleArr, $valueKeys)) return $this->packageError($field, $title, "错误");
        return true;
    }

    /**
     * Notes: 二维数组元素的key必须包含给定的规则里面的值
     * Author: Uncle-L
     * Date: 2021/10/21
     * Time: 16:13
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function arrayItemHas($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_array($value)) return $this->packageError($field, $title, "格式错误");
        if (!$value) return $this->packageError($field, $title, "不能为空");
        if (!$rule) return $this->packageError($field, $title, "验证规则[arrayItemHas]错误");
        $ruleArr = explode(',', $rule);
        foreach ($value as $kk => $vv) {
            if (!is_array($vv)) return $this->packageError($field, $title, "[{$kk}]格式错误");
            if (!$vv) return $this->packageError($field, $title, "[{$kk}]不能为空");
            $vvKeys = array_keys($vv);
            if (array_diff($ruleArr, $vvKeys)) return $this->packageError($field, $title, "[{$kk}]错误");
        }
        return true;
    }

    /**
     * Notes: 二维数组为空或元素的key必须包含给定的规则里面的值
     * Author: Uncle-L
     * Date: 2021/10/21
     * Time: 16:13
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function arrayEmptyOrItemHas($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_array($value)) return $this->packageError($field, $title, "格式错误");
        if (!$value) return true;
        if (!$rule) return $this->packageError($field, $title, "验证规则[arrayEmptyOrItemHas]错误");
        $ruleArr = explode(',', $rule);
        foreach ($value as $kk => $vv) {
            if (!is_array($vv)) return $this->packageError($field, $title, "[{$kk}]格式错误");
            if (!$vv) return $this->packageError($field, $title, "[{$kk}]不能为空");
            $vvKeys = array_keys($vv);
            if (array_diff($ruleArr, $vvKeys)) return $this->packageError($field, $title, "[{$kk}]错误");
        }
        return true;
    }

    /**
     * 验证表格table数据有效性
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function table($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_array($value)) return $this->packageError($field, $title, "格式错误");
        if (!$rule) return $this->packageError($field, $title, "验证规则[table]不能为空");
        $ruleRaw = base64_decode($rule);
        if (!is_json($ruleRaw, $cols, true)) return $this->packageError($field, $title, "验证规则[table]错误");
        $cols = array_column($cols, null, 'field');
        $cols = array_map(function ($col) {
            $col['title'] = "[{$col['title']}]";
            return $col;
        }, $cols);
        $colFields = array_keys($cols);
        $validate = clone $this;
        foreach ($value as $kk => $vv) {
            $rowDesc = '第' . ($kk + 1) . '行';
            if (!is_array($vv)) return $this->packageError($field, $title, "{$rowDesc}格式错误");
            if (!$vv) return $this->packageError($field, $title, "{$rowDesc}不能为空");
            $vvKeys = array_keys($vv);
            if (array_diff($colFields, $vvKeys)) {
                return $this->packageError($field, $title, "{$rowDesc}需所有表头传值");
            }
            // 验证器校验值
            if (!form_data_validate($validate, $cols, $vv, 'FormTableSubmit_' . rand_char(5))) {
                return $this->packageError($field, $title, $rowDesc . $validate->getError());
            }
        }
        return true;
    }

    /**
     * Notes: 验证地区数据有效性
     * Author: Uncle-L
     * Date: 2020/12/31
     * Time: 10:15
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function area($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        $accArr = ['province', 'city', 'district'];
        if (!$rule) $rule = "district";
        if (!in_array($rule, $accArr)) return $this->packageError($field, $title, "验证规则 area 允许参数[province|city|district]");
        $options = YunjArea::instance()->options();
        $acc = $rule;
        if (!is_array($value)) return $this->packageError($field, $title, "数据异常");

        if (!isset($value['province']) || !$value['province']) return $this->packageError($field, $title, "请选择省份");
        $provinceOptions = $options[0];
        if (!isset($provinceOptions[$value['province']])) return $this->packageError($field, $title, "省份数据异常");
        if ($acc === 'province') return true;

        if (!isset($value['city']) || !$value['city']) return $this->packageError($field, $title, "请选择城市");
        $cityOptions = $options["0,{$value['province']}"];
        if (!isset($cityOptions[$value['city']])) return $this->packageError($field, $title, "城市数据异常");
        if ($acc === 'city') return true;

        if (!isset($value['district']) || !$value['district']) return $this->packageError($field, $title, "请选择区/县");
        $cityOptions = $options["0,{$value['province']},{$value['city']}"];
        if (!isset($cityOptions[$value['district']])) return $this->packageError($field, $title, "区/县数据异常");

        return true;
    }

    /**
     * Notes: 验证汉字、字母、数字、下划线_、短横线-及空格
     * Author: Uncle-L
     * Date: 2020/12/20
     * Time: 17:27
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function chsDashSpace($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        $res = is_scalar($value) && 1 === preg_match('/^[\x{4e00}-\x{9fa5}A-Za-z0-9\_\-\s]+$/u', (string)$value);
        if (!$res) return $this->packageError($field, $title, "只能是汉字、字母、数字、下划线_、短横线-及空格组合");
        return true;
    }

    /**
     * Notes: 验证16进制色号
     * Author: Uncle-L
     * Date: 2020/12/20
     * Time: 17:27
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function hexColor($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        $res = is_scalar($value) && 1 === preg_match('/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/', (string)$value);
        if (!$res) return $this->packageError($field, $title, "错误");
        return true;
    }

    /**
     * 验证日期范围
     * @param $value
     * @param string $rule 分隔符
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function dateRange($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_string($value)) return $this->packageError($field, $title, "错误");
        $rule = $rule ?: Def::FORM_FIELD_DATE_RANGE;
        $sep = ' ' . $rule . ' ';
        [$startDate, $endDate] = explode($sep, $value);
        $startTime = strtotime($startDate);
        if ($startTime === false) return $this->packageError($field, $title, "错误");
        $endTime = strtotime($endDate);
        if ($endTime === false) return $this->packageError($field, $title, "错误");
        if ($startTime >= $endTime) return $this->packageError($field, $title, "错误");
        return true;
    }

    /**
     * Notes: 验证时间日期
     * Author: Uncle-L
     * Date: 2020/12/20
     * Time: 17:27
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function datetime($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        return is_datetime($value) ? true : $this->packageError($field, $title, "错误");
    }

    /**
     * 验证日期时间范围
     * @param $value
     * @param string $rule 分隔符
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function datetimeRange($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_string($value)) return $this->packageError($field, $title, "错误");
        $rule = $rule ?: Def::FORM_FIELD_DATE_RANGE;
        $sep = ' ' . $rule . ' ';
        [$startDate, $endDate] = explode($sep, $value);
        $startTime = strtotime($startDate);
        if (!$startTime || (date("s", $startTime) !== substr($startDate, -2))) return $this->packageError($field, $title, "错误");
        $endTime = strtotime($endDate);
        if (!$endTime || (date("s", $endTime) !== substr($endDate, -2))) return $this->packageError($field, $title, "错误");
        if ($startTime >= $endTime) return $this->packageError($field, $title, "错误");
        return true;
    }

    /**
     * Notes: 验证年份
     * Author: Uncle-L
     * Date: 2020/12/20
     * Time: 17:27
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function year($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        return is_year($value) ? true : $this->packageError($field, $title, "错误");
    }

    /**
     * 验证年份范围
     * @param $value
     * @param string $rule 分隔符
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function yearRange($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_string($value)) return $this->packageError($field, $title, "错误");
        $rule = $rule ?: Def::FORM_FIELD_DATE_RANGE;
        $sep = ' ' . $rule . ' ';
        [$startDate, $endDate] = explode($sep, $value);
        if (!is_year($startDate) || !is_year($endDate) || $startDate >= $endDate) return $this->packageError($field, $title, "错误");
        return true;
    }

    /**
     * Notes: 验证年月（示例：2019-09）
     * Author: Uncle-L
     * Date: 2020/12/20
     * Time: 17:27
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function yearMonth($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        return is_year_month($value) ? true : $this->packageError($field, $title, "错误");
    }

    /**
     * 验证年月范围
     * @param $value
     * @param string $rule 分隔符
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function yearMonthRange($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_string($value)) return $this->packageError($field, $title, "错误");
        $rule = $rule ?: Def::FORM_FIELD_DATE_RANGE;
        $sep = ' ' . $rule . ' ';
        [$startDate, $endDate] = explode($sep, $value);
        $startTime = strtotime($startDate);
        if (!$startTime || (date("m", $startTime) !== substr($startDate, -2))) return $this->packageError($field, $title, "错误");
        $endTime = strtotime($endDate);
        if (!$endTime || (date("m", $endTime) !== substr($endDate, -2))) return $this->packageError($field, $title, "错误");
        if ($startTime >= $endTime) return $this->packageError($field, $title, "错误");
        return true;
    }

    /**
     * Notes: 验证月份
     * Author: Uncle-L
     * Date: 2020/12/20
     * Time: 17:27
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function month($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        return is_month($value) ? true : $this->packageError($field, $title, "错误");
    }

    /**
     * 验证月份范围
     * @param $value
     * @param string $rule 分隔符
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function monthRange($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_string($value)) return $this->packageError($field, $title, "错误");
        $rule = $rule ?: Def::FORM_FIELD_DATE_RANGE;
        $sep = ' ' . $rule . ' ';
        [$startDate, $endDate] = explode($sep, $value);
        if (!is_month($startDate) || !is_month($endDate) || $startTime >= $endTime) return $this->packageError($field, $title, "错误");
        return true;
    }

    /**
     * Notes: 验证时间(01:01:01)
     * Author: Uncle-L
     * Date: 2020/12/20
     * Time: 17:27
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function time($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        return is_time($value) ? true : $this->packageError($field, $title, "错误");
    }

    /**
     * 验证日期/时间等数据范围
     * @param $value
     * @param string $rule 分隔符
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function timeRange($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_array($value)) {
            $valueData = [];
            is_json($value, $valueData, true);
            $value = $valueData;
        }
        if (!array_key_exists('start', $value) && !array_key_exists('end', $value)) {
            return $this->packageError($field, $title, "错误");
        }
        $start = $value['start'] ?? '';
        $end = $value['end'] ?? '';

        if (!$rule) return $this->packageError($field, $title, "验证规则[timeRange]的rule数据缺失");
        $checkCallName = 'is_' . uppercase_to_underline($rule);
        if (!function_exists($checkCallName)) return $this->packageError($field, $title, "验证规则[timeRange:{$rule}]错误");
        if ($start && !call_user_func($checkCallName, $start)) {
            return $this->packageError($field, $title, "开始时间错误");
        }
        if ($end && !call_user_func($checkCallName, $end)) {
            return $this->packageError($field, $title, "结束时间错误");
        }
        if ($start && $end) {
            $startNum = (int)preg_replace('/[^0-9]/', '', $start);
            $endNum = (int)preg_replace('/[^0-9]/', '', $end);
            if ($startNum > $endNum) return $this->packageError($field, $title, "开始时间不能大于结束时间");
        }
        return true;
    }

    /**
     * Notes: 逗号“,”间隔的汉字/字母/数字组合
     * Author: Uncle-L
     * Date: 2020/12/20
     * Time: 17:27
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function commaIntervalChsAlphaNum($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_string($value)) return $this->packageError($field, $title, "错误");
        return preg_match("/^[\x{4e00}-\x{9fa5}a-zA-Z0-9]+(?:,[\x{4e00}-\x{9fa5}a-zA-Z0-9]+)*$/u", $value) ? true
            : $this->packageError($field, $title, "错误");
    }

    /**
     * Notes: 逗号“,”间隔的正整数
     * Author: Uncle-L
     * Date: 2020/12/20
     * Time: 17:27
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function commaIntervalPositiveInt($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_string($value)) return $this->packageError($field, $title, "错误");
        return preg_match("/^[1-9]\d*(?:,[1-9]\d*)*$/", $value) ? true
            : $this->packageError($field, $title, "错误");
    }

    /**
     * Notes: 是否为json
     * Author: Uncle-L
     * Date: 2020/12/20
     * Time: 17:27
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function json($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_string($value) || !is_json($value)) return $this->packageError($field, $title, "错误");
        return true;
    }

    /**
     * Notes: 是否为url组成的数组
     * Author: Uncle-L
     * Date: 2021/10/21
     * Time: 16:13
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function urls($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_array($value)) return $this->packageError($field, $title, "格式错误");
        if (!$value) return true;

        for ($i = 0; $i < count($value); $i++) {
            $v = $value[$i];
            if (!$v || !is_string($v) || !is_url($v)) {
                $num = $i + 1;
                return $this->packageError($field, $title, "第{$num}个地址格式错误");
            }
        }
        return true;
    }

    /**
     * Notes: 是否为url（注意：系统提供的方法不支持地址中含有中文等字符）
     * Author: Uncle-L
     * Date: 2021/10/21
     * Time: 16:13
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function url($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_url($value)) return $this->packageError($field, $title, "地址格式错误");
        return true;
    }

    /**
     * Notes: 是否为uri（/开头）
     * Author: Uncle-L
     * Date: 2021/10/21
     * Time: 16:13
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function uri($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        if (!is_uri($value)) return $this->packageError($field, $title, "地址格式错误");
        return true;
    }

}